package edu.ubc.javascript;

import static edu.ubc.javascript.ASTFactory.name;

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;

import com.google.javascript.jscomp.Compiler;
import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.JSSourceFile;
import com.google.javascript.jscomp.NodeTraversal;
import com.google.javascript.jscomp.mozilla.rhino.ast.AstNode;
import com.google.javascript.jscomp.mozilla.rhino.ast.FunctionNode;
import com.google.javascript.rhino.Node;

public class Main {

	public static void main(String[] args) throws Exception {
		Main main = new Main();
		main.transformAll(args);
	}
	
	public void transformAll(String[] args) throws Exception {
		for(String fileName : args) {
			JSSourceFile input = JSSourceFile.fromFile(fileName);
			PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(fileName+".out")));
			transform(input, pw);
			pw.close();
		}
	}
	
	public void transform(ReflectiveAstTransformer tx, PrintWriter pw, Compiler compiler) throws Exception {
		Node node = compiler.getRoot().getLastChild();

		ScopeVisitor sv = new ScopeVisitor();
		NodeTraversal traversal = new NodeTraversal(compiler, sv);
		traversal.traverse(compiler.getRoot().getLastChild());

		ClosureCheck cc = new ClosureCheck(sv);
		traversal = new NodeTraversal(compiler, cc);
		traversal.traverse(compiler.getRoot().getLastChild());

		ASTFactory cg = new ASTFactory();
		AstNode ast = cg.add(node);
		
		FunctionNodeClosureInfoVisitor fnciv = new FunctionNodeClosureInfoVisitor(tx);
		ast.visit(fnciv);
		
		OpenClosuresVisitor osv = new OpenClosuresVisitor(fnciv, tx);
		ast.visit(osv);
		tx.commit();
		
		List<String> names = new LinkedList<String>();
		for(FunctionNode fn : osv.innerFunctions) {
			String src = fn.toSource();
			tx.insertAfter(ast, ast.getLastChild(), name(src+"\n"));
			names.add(fn.getName());
		}

		tx.commit();

		String outputStr = ast.toSource();
		pw.append(outputStr);
		pw.flush();
		
		for(FunctionNodeScopeInfo fnci : fnciv.getInfo().values()) {
			System.out.println(fnci.getName() + ": " + fnci.isOnClosurePath());
		}
	}
	
	public void transform(JSSourceFile input, PrintWriter pw) throws Exception {
		ReflectiveAstTransformer tx = new ReflectiveAstTransformer();
		PrintStream output = System.out;
		Compiler compiler = new Compiler(output);
		JSSourceFile[] externs = new JSSourceFile[0];
		JSSourceFile[] inputs = {input};
		CompilerOptions options = new CompilerOptions();
		compiler.compile(externs, inputs, options);
		transform(tx, pw, compiler);
	}

}
